# -*- coding: utf-8 -*-

"""
    Forrest Cao   2022.11
"""

import sys,os,time, datetime, copy
import logging
import json
import tornado.web
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
import threading
import requests
import base64
import hashlib
import utils
from utils import xprint
import dbm

# json 序列化 datetime
class DateEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj,datetime.datetime):
            return obj.strftime("%Y-%m-%d %H:%M:%S")
        else:
            return json.JSONEncoder.default(self,obj)

class APIM():
    def __init__(self):
        self.name = 'XueQing'
        #生成 api 映射map
        self.prefix = ''
        self.api_map = {}
    def register_api(self,method='-'):
        def wrapper(_func):
            full_url = '%s%s' % (self.prefix,method)
            #xprint(_func)  #<function OMS.api_get_game_params at 0x000001F1A0484AF8>
            fs = getattr(_func,'__name__') # api_get_game_params
            self.api_map[full_url] = _func
            #print('Register api[ %-24s ]    func[ %-28s ]' % (full_url,fs))
        return wrapper
    def get_func_by_url(self, _path):
        if _path in self.api_map:
            return self.api_map[_path]
        return None
apim = APIM()

executor_count = 5
thread_dbm = {}
def get_thread_dbm(_thread_id):
    key = 'thread_%d' % _thread_id
    if key in thread_dbm:
        return thread_dbm[key]
    db = dbm.DBM()
    db.conn_db()
    db_xq = dbm.dbm_xqing(db)
    thread_dbm[key] = db_xq
    return db_xq

class BaseHandler(tornado.web.RequestHandler):
    def __init__( self, *args, **kwargs ):
        tornado.web.RequestHandler.__init__( self, *args, **kwargs )
    def set_default_headers(self):
        self.set_header('Access-Control-Allow-Origin', '*')
        self.set_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
        self.set_header('Access-Control-Max-Age', 1000)
        self.set_header('Access-Control-Allow-Headers', '*')
        #self.set_header("Access-Control-Allow-Headers", "content-type,x-requested-with,token,signature")
        #self.set_header('Content-type', 'application/json')
    def get(self):
        #self.write_error(404)
        self.redirect('/static/login.html')
    def write_error(self, status_code, **kwargs):
        if status_code == 404:
            self.redirect('/static/login.html')
        else:
            self.write('error:' + str(status_code))

'''
class IndexHandle(BaseHandler):
    def get(self):
        self.redirect('/static/login.html')
'''

#登录
@apim.register_api('/api/login')
def login(_handle,_req,_resp):
    username = _req['username']
    password = _req['password']
    _resp['body'] = {}
    (rcode,rmsg,body) = _handle.dbxq.login(username, password)
    if rcode == 0:
        _resp['token'] = body['token']
        _resp['is_admin'] = body['is_admin']
    return (rcode,rmsg)

#获取考试的数据列表
@apim.register_api('/api/examinationRecords')
def examinationRecords(_handle,_req,_resp):
    token = utils.reverse_str(_req['token'])
    _resp['body'] = []
    (rcode,rmsg,exam_list) = _handle.dbxq.get_examination_records(token)
    if rcode == 0:
        _resp['body'] = exam_list
    return (rcode,rmsg)

#查询考试的详情
@apim.register_api('/api/examinationDetail')
def examinationDetail(_handle,_req,_resp):
    token = utils.reverse_str(_req['token'])
    eid = int(_req['exam_id'])
    _resp['body'] = []
    (rcode,rmsg,exam_detail) = _handle.dbxq.get_examination_detail(token,eid)
    if rcode == 0:
        _resp['body'] = exam_detail
    return (rcode,rmsg)

    _resp['body'] = {
        'exam_id': exam_id,
        'title': '人分七年级3班秋季期中考试',
        'categorys': [
            { 'cid':0, 'name':"总分" },
            { 'cid':1, 'name':"语文" },
            { 'cid':2, 'name':"数学" },
            { 'cid':3, 'name':"英语" },
            { 'cid':4, 'name':"生物" },
            { 'cid':5, 'name':"历史" },
            { 'cid':6, 'name':"地理" },
            { 'cid':7, 'name':"道法" }
        ],
        'results': [
            {
                'category':0,
                'stat': [
                    { 'cid':1, 'result':51.6, 'total':100 },
                    { 'cid':2, 'result':52.6, 'total':100 },
                    { 'cid':3, 'result':55.7, 'total':100 },
                    { 'cid':4, 'result':55.6, 'total':100 },
                    { 'cid':5, 'result':54.9, 'total':100 },
                    { 'cid':6, 'result':55.6, 'total':100 },
                    { 'cid':7, 'result':55.9, 'total':100 }
                ],
                'sum_details': [
                    { 'idx': 0, 'cid': 0, 'result':255,  'rank': 8,  'average_result': 357.5, 'max_result': 398 },
                    { 'idx': 1, 'cid': 1, 'result':66.5, 'rank': 6,  'average_result': 57.5,  'max_result': 97.5 },
                    { 'idx': 2, 'cid': 2, 'result':67.5, 'rank': 5,  'average_result': 61,    'max_result': 93 },
                    { 'idx': 3, 'cid': 3, 'result':45,   'rank': 3,  'average_result': 65,    'max_result': 88.5 },
                    { 'idx': 4, 'cid': 4, 'result':56.5, 'rank': 11, 'average_result': 58.5,  'max_result': 92.5 },
                    { 'idx': 5, 'cid': 5, 'result':54,   'rank': 21, 'average_result': 63.5,  'max_result': 94 },
                    { 'idx': 6, 'cid': 6, 'result':83,   'rank': 32, 'average_result': 77.3,  'max_result': 96 },
                    { 'idx': 7, 'cid': 7, 'result':75,   'rank': 40, 'average_result': 76.5,  'max_result': 98.5 }
                ],
                'details': [
                    { 'idx': 1, 'result':60.5, 'me':0 },
                    { 'idx': 2, 'result':55.5, 'me':1 },
                    { 'idx': 3, 'result':55.5, 'me':0 },
                    { 'idx': 4, 'result':55.5, 'me':0 },
                    { 'idx': 5, 'result':55.5, 'me':0 },
                    { 'idx': 6, 'result':55.5, 'me':0 },
                    { 'idx': 7, 'result':55.5, 'me':0 },
                    { 'idx': 8, 'result':55.5, 'me':0 },
                    { 'idx': 9, 'result':55.5, 'me':1 },
                    { 'idx': 10, 'result':55.5, 'me':0 },
                    { 'idx': 11, 'result':55.5, 'me':0 },
                    { 'idx': 12, 'result':55.5, 'me':0 },
                    { 'idx': 13, 'result':55.5, 'me':0 },
                    { 'idx': 14, 'result':55.5, 'me':0 }
                ]
            },
            {
                'category':1,
                'stat': {
                    'result':60.5, 'seq': 6, 'average_result': 57.5, 'max_result': 78.1
                },
                'details': [
                    { 'idx': 0, 'result':55.5, 'me':0 },
                    { 'idx': 1, 'result':60.5, 'me':0 },
                    { 'idx': 2, 'result':55.5, 'me':1 },
                    { 'idx': 3, 'result':55.5, 'me':0 },
                    { 'idx': 4, 'result':55.5, 'me':0 },
                    { 'idx': 5, 'result':55.5, 'me':0 },
                    { 'idx': 6, 'result':55.5, 'me':0 },
                    { 'idx': 7, 'result':55.5, 'me':0 },
                    { 'idx': 8, 'result':55.5, 'me':0 },
                    { 'idx': 9, 'result':55.5, 'me':1 },
                    { 'idx': 10, 'result':55.5, 'me':0 },
                    { 'idx': 11, 'result':55.5, 'me':0 },
                    { 'idx': 12, 'result':55.5, 'me':0 },
                    { 'idx': 13, 'result':55.5, 'me':0 },
                    { 'idx': 14, 'result':55.5, 'me':0 }
                ]
            },
            {
                'category':2,
                'stat': {
                    'result':66.9, 'seq': 8, 'average_result': 57.5, 'max_result': 78.1
                },
                'details': [
                    { 'idx': 0, 'result':66.9, 'me':0 },
                    { 'idx': 1, 'result':66.9, 'me':0 },
                    { 'idx': 2, 'result':66.9, 'me':1 },
                    { 'idx': 3, 'result':66.9, 'me':0 },
                    { 'idx': 4, 'result':66.9, 'me':0 },
                    { 'idx': 5, 'result':66.9, 'me':0 },
                    { 'idx': 6, 'result':66.9, 'me':0 },
                    { 'idx': 7, 'result':66.9, 'me':0 },
                    { 'idx': 8, 'result':66.9, 'me':0 },
                    { 'idx': 9, 'result':66.9, 'me':1 },
                    { 'idx': 10, 'result':66.9, 'me':0 },
                    { 'idx': 11, 'result':66.9, 'me':0 },
                    { 'idx': 12, 'result':66.9, 'me':0 },
                    { 'idx': 13, 'result':66.9, 'me':0 },
                    { 'idx': 14, 'result':66.9, 'me':0 }
                ]
            },
            {
                'category':3,
                'stat': {
                    'result':88.1, 'seq': 8, 'average_result': 57.5, 'max_result': 78.1
                },
                'details': [
                    { 'idx': 0, 'result':88.1, 'me':0 },
                    { 'idx': 1, 'result':88.1, 'me':0 },
                    { 'idx': 2, 'result':88.1, 'me':1 },
                    { 'idx': 3, 'result':88.1, 'me':0 },
                    { 'idx': 4, 'result':88.1, 'me':0 },
                    { 'idx': 5, 'result':88.1, 'me':0 },
                    { 'idx': 6, 'result':88.1, 'me':0 },
                    { 'idx': 7, 'result':88.1, 'me':0 },
                    { 'idx': 8, 'result':88.1, 'me':0 },
                    { 'idx': 9, 'result':88.1, 'me':1 },
                    { 'idx': 10, 'result':88.1, 'me':0 },
                    { 'idx': 11, 'result':88.1, 'me':0 },
                    { 'idx': 12, 'result':88.1, 'me':0 },
                    { 'idx': 13, 'result':88.1, 'me':0 },
                    { 'idx': 14, 'result':88.1, 'me':0 }
                ]
            }
        ]
    }
    return (0,'success')

#验证提交的考试识别编码
@apim.register_api('/api/verifyExaminationCode')
def verifyExaminationCode(_handle,_req,_resp):
    token = utils.reverse_str(_req['token'])
    examinationCode = _req['examinationCode']
    _resp['body'] = {}
    '''
    验证 考试识别编码， 然后新增这条记录，成绩为0
    '''
    (rcode,rmsg,exam_results) = _handle.dbxq.verify_examination_code(token,examinationCode)
    if rcode == 0:
        _resp['body'] = exam_results
    return (rcode,rmsg)

#获取指定的某次考试的成绩
@apim.register_api('/api/getExaminationResults')
def getExaminationResults(_handle,_req,_resp):
    token = utils.reverse_str(_req['token'])
    eid = int(_req['exam_id'])
    _resp['body'] = []
    (rcode,rmsg,body) = _handle.dbxq.get_examination_results(token,eid)
    if rcode == 0:
        _resp['body'] = body
    return (rcode,rmsg)

#提交考试成绩
@apim.register_api('/api/submitExaminationResults')
def submitExaminationResults(_handle,_req,_resp):
    token = utils.reverse_str(_req['token'])
    eid = int(_req['exam_id'])
    results = _req['results']
    _resp['body'] = []
    (rcode,rmsg,body) = _handle.dbxq.submit_examination_results(token,eid,results)
    if rcode == 0:
        _resp['body'] = body
    return (rcode,rmsg)

#删除登记的考试成绩
@apim.register_api('/api/deleteExaminationResults')
def deleteExaminationResults(_handle,_req,_resp):
    token = utils.reverse_str(_req['token'])
    eid = int(_req['exam_id'])
    _resp['body'] = []
    (rcode,rmsg,body) = _handle.dbxq.delete_examination_results(token,eid)
    if rcode == 0:
        _resp['body'] = body
    return (rcode,rmsg)



#管理 - 查询所有考试列表
@apim.register_api('/api/mgr_examinations')
def mgr_examinations(_handle,_req,_resp):
    _resp['body'] = []
    (rcode,rmsg,exam_list) = _handle.dbxq.get_examinations()
    if rcode == 0:
        _resp['body'] = exam_list
    return (rcode,rmsg)

#管理 - 查询学校列表
@apim.register_api('/api/mgr_schools')
def mgr_schools(_handle,_req,_resp):
    _resp['body'] = []
    (rcode,rmsg,school_list) = _handle.dbxq.get_schools()
    if rcode == 0:
        _resp['body'] = school_list
    return (rcode,rmsg)

#管理 - 查询所有科目列表
@apim.register_api('/api/mgr_courses')
def mgr_courses(_handle,_req,_resp):
    _resp['body'] = []
    (rcode,rmsg,course_list) = _handle.dbxq.get_courses()
    if rcode == 0:
        _resp['body'] = course_list
    return (rcode,rmsg)

#管理 - 查询考试信息
@apim.register_api('/api/mgr_examinationDetail')
def mgr_examination_detail(_handle,_req,_resp):
    eid = int(_req['exam_id'])
    _resp['body'] = {}
    (rcode,rmsg,exam_detail) = _handle.dbxq.examination_detail(eid)
    if rcode == 0:
        _resp['body'] = exam_detail
    return (rcode,rmsg)

#管理 - 增加考试 / 修改考试
@apim.register_api('/api/mgr_addExamination')
def mgr_add_examination(_handle,_req,_resp):
    exam = _req
    _resp['body'] = []
    (rcode,rmsg,body) = _handle.dbxq.add_examination(exam)
    if rcode == 0:
        _resp['body'] = body
    return (rcode,rmsg)

#管理 - 删除考试
@apim.register_api('/api/mgr_deleteExamination')
def mgr_delete_examination(_handle,_req,_resp):
    eid = int(_req['exam_id'])
    _resp['body'] = []
    (rcode,rmsg,body) = _handle.dbxq.del_examination(eid)
    if rcode == 0:
        _resp['body'] = body
    return (rcode,rmsg)

#管理 - 查询用户密码
@apim.register_api('/api/mgr_getMember')
def mgr_get_member(_handle,_req,_resp):
    username = _req['username']
    _resp['body'] = []
    (rcode,rmsg,body) = _handle.dbxq.get_member(username)
    if rcode == 0:
        _resp['body'] = body
    return (rcode,rmsg)


class EntryHandle(BaseHandler):
    global executor_count
    executor = ThreadPoolExecutor(executor_count)   #起线程池，由当前RequestHandler持有

    @run_on_executor
    def post(self):
        self.dbxq = get_thread_dbm(threading.current_thread().ident)
        method = self.request.path
        #print(method)
        func = apim.get_func_by_url(method)
        if func == None:
            raise tornado.web.HTTPError(404)
        req_s = self.request.body.decode()
        logging.info('Rx  ------->    %s\n%s' % (method,req_s))

        req = json.loads(req_s)
        resp = {}
        (rcode,rmsg) = func(self,req,resp)
        resp['time'] = int(time.time())
        resp['rcode'] = rcode
        resp['rmsg'] = rmsg
        resp_s = json.dumps(resp,cls=DateEncoder)
        if rcode != 0:
            logging.error('EEROR  process method[ %s ] failed. rcode[ %d ]' % (method,rcode))
            #logging.error('EEROR  process method[ %s ] failed. rcode[ %d ]\nRx --->  %s\nTx --->  %s' % (method,rcode,req_s,resp_s))
        logging.info('Tx  ------->    %s  rcode[ %d ]\n%s' % (method,rcode,resp_s))
        #time.sleep(1)
        self.write(resp_s)


if __name__ == "__main__":
    pass

